/* -*-C++-*- */

/** \defgroup irdefs Intermediate Representation */
/** \addtogroup irdefs
  * @{
  */

/*
   This file contains the base classes needed for the compiler IR:
   - all interfaces
   - most basic abstract classes
   - some simple leaf classes used frequently
*/

#emit
#include "frontends/common/constantParsing.h"
#end

/// a value that can be evaluated at compile-time
interface CompileTimeValue {
    bool equiv(const CompileTimeValue& other) const {
        return this->getNode()->equiv(*other.getNode());
    }
}

/// Base class for P4 types
abstract Type {
    static const cstring minSizeInBits;
    static const cstring minSizeInBytes;
    static const cstring maxSizeInBits;
    static const cstring maxSizeInBytes;
#emit
    typedef Type_Unknown        Unknown;
    typedef Type_Boolean        Boolean;
    typedef Type_Bits           Bits;
    typedef Type_String         String;
    typedef Type_Varbits        Varbits;
    typedef Type_Void           Void;
#end
    /// Well-defined only for types with fixed width
    virtual int width_bits() const { return 0; }
    /// When possible returns the corresponding type that can be inserted
    /// in a P4 program; may return a Type_Name
    virtual const Type* getP4Type() const = 0;
}

/// Implemented by all types that may be generic:
/// Control, Extern, Method, Package, and Parser
interface IMayBeGenericType {
    /// Allows the retrieval of type parameters
    virtual const TypeParameters* getTypeParameters() const = 0;
}

/// Implemented by objects with an 'apply' method: Parser, Control, Table
interface IApply {
    static const cstring applyMethodName;
    /// @returns the type signature of the apply method
    virtual const Type_Method* getApplyMethodType() const = 0;
    virtual const ParameterList* getApplyParameters() const = 0;
}

/// base class for namespaces
interface INamespace {
    virtual Util::Enumerator<IDeclaration> *getDeclarations() const = 0;
}

/// Does not allow two declarations with the same name
interface ISimpleNamespace : INamespace {
    virtual IDeclaration getDeclByName(cstring name) const = 0;
}

/// A general namespace can have multiple declarations with the same name
/// E.g., an extern can have multiple methods with the same name.
interface IGeneralNamespace : INamespace {
    virtual Util::Enumerator<IDeclaration>* getDeclsByName(cstring name) const;
    /// prints an error if it finds duplicate names
    void checkDuplicateDeclarations() const;
    validate{ checkDuplicateDeclarations(); }
}

// A "namespace" that really consists of several nested namespaces
// Note that it might also contain a Simple or General namespace nested in these
interface INestedNamespace : INamespace {
    virtual std::vector<INamespace> getNestedNamespaces() const = 0;
    Util::Enumerator<IDeclaration> *getDeclarations() const;
}

/// Interface implemented by something that can be called
/// like a function.
interface IFunctional {
    /// The parameters of the functional object
    virtual ParameterList getParameters() const = 0;
    /// Returns true if the parameters can be matched with the
    /// supplied arguments.
    bool callMatches(Vector<Argument> arguments) const;
}

/// Implemented by things that look like type variables
interface ITypeVar {
    virtual cstring getVarName() const = 0;
    inline Type asType() const { return to<Type>(); }
    virtual int getDeclId() const = 0;
}

/// Implemented by P4Parser, P4Control and Type_Package
interface IContainer : IMayBeGenericType, IDeclaration, IFunctional {
    /// The type of the constructor as a method
    virtual Type_Method getConstructorMethodType() const = 0;
    virtual ParameterList getConstructorParameters() const = 0;
    // The IFunctional interface
    ParameterList getParameters() const { return getConstructorParameters(); }
}

/// This represents a primitive type
/// (called base type in the spec)
abstract Type_Base : Type {
    const Type* getP4Type() const override { return this; }
}

/// This is needed by Expression
class Type_Unknown : Type_Base {
#nodbprint
    static Type_Unknown get();
    toString{ return "Unknown type"; }
}

/// A statement or a declaration
abstract StatOrDecl {}

/// Two declarations with the same name are not necessarily the same declaration.
/// That's why declid is used to distinguish them.
abstract Declaration : StatOrDecl, IDeclaration {
    ID          name;
    int declid = nextId++;
    ID getName() const override { return name; }
    equiv { return name == a.name; /* ignore declid */ }
 private:
    static int nextId;
 public:
    toString { return externalName(); }
}

/// A declaration which introduces a type.
/// Two declarations with the same name are not the same declaration
/// That's why declid is used to distinguish them.
/// (We don't use multiple inheritance, so we can't
/// inherit both Type and Declaration.)
abstract Type_Declaration : Type, IDeclaration {
    ID name;
    int declid = nextId++;
    ID getName() const override { return name; }
    equiv { return name == a.name; /* ignore declid */ }
 private:
    static int nextId;
 public:
    toString { return externalName(); }
    const Type* getP4Type() const override { return new Type_Name(name); }
}

/// base class for expressions
abstract Expression {
    /// Note that the type field is not visited.
    /// Most P4_16 passes don't use this field.
    /// It is a used to hold the result of TypeInferencing for the expression.
    /// It is used by the P4_14 front-end and by some back-ends.
    /// It is not visited by the visitors by default (can be visited explicitly in preorder)
    optional Type type = Type::Unknown::get();
    visit_children { (void)v; }
#apply
}

abstract Operation : Expression {
    virtual int getPrecedence() const = 0;
    virtual cstring getStringOp() const = 0;
#emit
    typedef Operation_Unary Unary;
    typedef Operation_Binary Binary;
    typedef Operation_Relation Relation;
#end
    toString{ return getStringOp(); }
}

/// Currently paths can be absolute (starting with a dot) or relative
/// (just an identifier).  In a previous design paths could have
/// multiple components.
class Path {
    ID   name;
    optional bool absolute = false;
    Path { if (!srcInfo) srcInfo = name.srcInfo; }
    bool isDontCare() const { return name.isDontCare(); }
    toString{
        // This is the ORIGINAL name the user used
        if (absolute)
            return cstring(".") + name.toString();
        return name.toString();
    }
    cstring asString() const {
        // The CURRENT internal name
        if (absolute)
            return cstring(".") + name;
        return name;
    }
    dbprint { out << name; }
    validate { BUG_CHECK(!name.name.isNullOrEmpty(), "Empty path"); }
}

/// Handy class used in several NamedMaps
class NamedExpression : Declaration {
    Expression expression;
}

/// A token in an unparsed annotation.
/// This should really be P4::P4Parser::symbol_type, but p4parser.hpp depends
/// on the IR in a way that makes it difficult to #include in this file.
class AnnotationToken {
    int token_type;  // P4Parser::token_type in disguise.
    cstring text;
    optional NullOK UnparsedConstant* constInfo = nullptr;
    dbprint { out << text; }
}

/// Annotations are used to provide additional information to the compiler
/// Most P4 entities can be optionally annotated
class Annotation {
    ID name;

    /// An unparsed annotation body
    inline Vector<AnnotationToken> body;

    /// Whether the annotation body needs to be parsed.
    /// Invariant: if this is true, then expr and kv must both be empty. If the
    /// annotation is compiler-generated (e.g., derived from a P4₁₄ pragma),
    /// then needsParsing will be false and the body will be empty, but expr or
    /// kv may be populated.
    bool needsParsing;

    /// Annotations that are simple expressions
    inline Vector<Expression> expr;

    /// Annotations described as key-value pairs
    inline IndexedVector<NamedExpression> kv;

    /// If this is true this is a structured annotation, and there are some
    /// constraints on its contents.
    bool structured;

    Annotation { if (!srcInfo) srcInfo = name.srcInfo; }

    /// For annotations parsed from P4-16 source.
    Annotation(Util::SourceInfo si, ID n, const Vector<AnnotationToken> &a)
    : Node(si), name(n), body(a), needsParsing(true), structured(false) {}
    // Used by JSON loader
    Annotation(Util::SourceInfo si, ID n, const Vector<AnnotationToken> &a, bool structured)
    : Node(si), name(n), body(a), needsParsing(true), structured(structured) {}

    // The remaining constructors are for compiler-generated annotations.
    Annotation(Util::SourceInfo si, ID n,
               const std::initializer_list<const Expression *> &a, bool structured = false)
    : Node(si), name(n), needsParsing(false), expr(a), structured(structured) {}
    Annotation(Util::SourceInfo si, ID n, const IR::Vector<Expression> &a, bool structured = false)
    : Node(si), name(n), needsParsing(false), expr(a), structured(structured) {}
    Annotation(Util::SourceInfo si, ID n, const IndexedVector<NamedExpression> &kv,
               bool structured = false)
    : Node(si), name(n), needsParsing(false), kv(kv), structured(structured) {}
    Annotation(ID n, const std::initializer_list<const Expression *> &a, bool structured = false)
    : name(n), needsParsing(false), expr(a), structured(structured) {}
    Annotation(ID n, const IR::Vector<Expression> &a, bool structured = false)
    : name(n), needsParsing(false), expr(a), structured(structured) {}
    Annotation(ID n, intmax_t v, bool structured = false)
    : name(n), needsParsing(false), structured(structured) {
        expr.push_back(new Constant(v)); }
    Annotation(ID n, cstring v, bool structured = false)
    : name(n), needsParsing(false), structured(structured) {
        expr.push_back(new StringLiteral(v)); }

    static const cstring nameAnnotation;  /// Indicates the control-plane name.
    static const cstring tableOnlyAnnotation;  /// Action cannot be a default_action.
    static const cstring defaultOnlyAnnotation;  /// action can only be a default_action.
    static const cstring atomicAnnotation;  /// Code should be executed atomically.
    static const cstring hiddenAnnotation;  /// Object should not be exposed to the control-plane.
    static const cstring lengthAnnotation;  /// P4-14 annotation for varbit fields.
    static const cstring optionalAnnotation;  /// Optional parameter annotation
    static const cstring pkginfoAnnotation;  /// Package documentation annotation.
    static const cstring deprecatedAnnotation;  /// Deprecation annotation.
    static const cstring synchronousAnnotation;  /// Synchronous annotation.
    static const cstring pureAnnotation;  /// extern function/method annotation.
    static const cstring noSideEffectsAnnotation;  /// extern function/method annotation.
    static const cstring noWarnAnnotation;  /// noWarn annotation.
    static const cstring matchAnnotation;  /// Match annotation (for value sets).
    static const cstring fieldListAnnotation;  /// Used for recirculate, etc.
    toString{ return cstring("@") + name; }
    validate{
        BUG_CHECK(!name.name.isNullOrEmpty(), "empty annotation name");
        BUG_CHECK(!(needsParsing && !expr.empty()),
            "unparsed annotation body with non-empty expr");
        BUG_CHECK(!(needsParsing && !kv.empty()),
            "unparsed annotation body with non-empty kv");
    }

    /// Extracts name value from a name annotation
    cstring getName() const;
    /// Extracts a single string argument; error if the argument is not a string
    cstring getSingleString() const;

#emit
    enum class Kind {
        Unstructured,
        StructuredEmpty,
        StructuredKVList,
        StructuredExpressionList
    };

    Kind annotationKind() const {
        if (!structured)
            return Kind::Unstructured;
        if (expr.size())
            return Kind::StructuredExpressionList;
        if (kv.size())
            return Kind::StructuredKVList;
        return Kind::StructuredEmpty;
    }
#end
}

/// There can be several annotations with the same "name", so this is a vector.
// FIXME -- get rid of this class -- classes with annotations should have an
// inline Vector<Annotation> instead (remove useless indirection)
class Annotations {
    optional inline Vector<Annotation> annotations;
    Annotations { if (!srcInfo && !annotations.empty()) srcInfo = annotations[0]->srcInfo; }
    static Annotations *empty;  // FIXME -- should be const
    size_t size() const { return annotations.size(); }
    // Get the annotation with the specified name or nullptr.
    // There should be at most one annotation with this name.
    Annotation getSingle(cstring name) const { return get(annotations, name); }
    Annotations add(Annotation annot) {
        if (annot->srcInfo) srcInfo += annot->srcInfo;
        annotations.push_back(annot);
        return this; }
    Annotations add(Annotation annot) const { return clone()->add(annot); }
    Annotations addAnnotation(cstring name, Expression expr, bool structured = false) {
        return add(new Annotation(name, Vector<Expression>(expr), structured)); }
    Annotations addAnnotation(cstring name, Expression expr, bool structured = false) const {
        return add(new Annotation(name, Vector<Expression>(expr), structured)); }
    // Add annotation if another annotation with the same name is not
    // already present.
    Annotations addAnnotationIfNew(cstring name, Expression expr, bool structured = false) const {
        return getSingle(name) ? this : addAnnotation(name, expr, structured); }
    Annotations append(Annotations other) {
        if (other == nullptr)
            return this;
        for (auto a : other->annotations)
            add(a);
        return this;
    }
    // If annotations with the same name are already present, remove them.
    // Add this annotation.
    Annotations addOrReplace(cstring name, Expression expr, bool structured = false) const {
        auto rv = clone();
        remove_if(rv->annotations, [name](const Annotation *a)->bool { return a->name == name; });
        rv->annotations.push_back(new Annotation(name, Vector<Expression>(expr), structured));
        return rv; }
#emit
    typedef std::function<bool(const IR::Annotation*)> Filter;
#end
    Annotations where(Filter func) const {
        auto rv = empty->clone();
        std::copy_if(annotations.begin(), annotations.end(),
                     std::back_insert_iterator<Vector<Annotation>>(rv->annotations), func);
        if (rv->annotations.size() == annotations.size()) return this;
        return rv; }
    validate{ annotations.check_null(); }
    dbprint { for (auto a : annotations) out << a << ' '; }
}

/// Implemented by all objects that can have annotations
interface IAnnotated {
    virtual Annotations getAnnotations() const = 0;
    Annotation getAnnotation(cstring name) const override {
        return getAnnotations()->getSingle(name); }
}

interface IInstance {
    virtual cstring Name() const = 0;
    virtual Type getType() const = 0;
}

/// An argument to a function call (or constructor call)
/// Arguments may have optional names
class Argument {
    /// If an argument has no name the name.name is nullptr.
    optional ID name;
    Expression expression;
    Argument { if (!srcInfo && expression) srcInfo = expression->srcInfo; }
    dbprint { out << (name.name.isNullOrEmpty() ? "" : name.name + " = ") << expression; }
    validate { CHECK_NULL(expression); }
    toString{
        cstring result = "";
        if (!name.name.isNullOrEmpty())
            result = name.toString() + " = ";
        return result + expression->toString();
    }
}

/** @} *//* end group irdefs */
